package com.mjk.manager.magicapi.plugin.swagger.entity;

import java.util.*;

/**
 * Swagger接口信息
 *
 * @author mxd
 */
public class SwaggerEntity {

    private String swagger = "2.0";

    private String host;

    private String basePath;

    private Info info;

    private final Map<String, Object> securityDefinitions = new HashMap<>();

    private final List<Map<String, Object>> security = new ArrayList<>();

    private final Set<Tag> tags = new TreeSet<>(Comparator.comparing(Tag::getName));

    private final Map<String, Object> definitions = new HashMap<>();

    private final Map<String, Map<String, Path>> paths = new HashMap<>();

    private static Map<String, Object> doProcessSchema(Object target) {
        Map<String, Object> result = new HashMap<>(3);
        result.put("type", getType(target));
        if (target instanceof List) {
            List<?> targetList = (List<?>) target;
            if (targetList.size() > 0) {
                result.put("items", doProcessSchema(targetList.get(0)));
            } else {
                result.put("items", Collections.emptyList());
            }
        } else if (target instanceof Map) {
            Set<Map.Entry> entries = ((Map) target).entrySet();
            Map<String, Map<String, Object>> properties = new HashMap<>(entries.size());
            for (Map.Entry entry : entries) {
                properties.put(Objects.toString(entry.getKey()), doProcessSchema(entry.getValue()));
            }
            result.put("properties", properties);
        } else {
            result.put("example", target == null ? "" : target);
            result.put("description", target == null ? "" : target);
        }
        return result;
    }

    private static String getType(Object object) {
        if (object instanceof Number) {
            return "number";
        }
        if (object instanceof String) {
            return "string";
        }
        if (object instanceof Boolean) {
            return "boolean";
        }
        if (object instanceof List) {
            return "array";
        }
        if (object instanceof Map) {
            return "object";
        }
        return "string";
    }

    public static Map<String, Object> createParameter(boolean required, String name, String in, String type, String description, Object example) {
        Map<String, Object> parameter = new HashMap<>();
        parameter.put("required", required);
        parameter.put("name", name);
        parameter.put("in", in);
        parameter.put("description", description);

        if ("body".equalsIgnoreCase(in)) {
            Map<String, Object> schema = new HashMap<>();
            schema.put("type", type);
            schema.put("example", example);
            parameter.put("schema", schema);
        } else {
            parameter.put("x-example", example);
            parameter.put("type", type);
        }
        return parameter;
    }

    public Info getInfo() {
        return info;
    }

    public void setInfo(Info info) {
        this.info = info;
    }

    public void addPath(String path, String method, Path pathInfo) {
        Map<String, Path> map = paths.computeIfAbsent(path, k -> new HashMap<>());
        map.put(method.toLowerCase(), pathInfo);
    }

    public void addTag(String name, String description) {
        this.tags.add(new Tag(name, description));
    }

    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public String getSwagger() {
        return swagger;
    }

    public void setSwagger(String swagger) {
        this.swagger = swagger;
    }

    public String getBasePath() {
        return basePath;
    }

    public void setBasePath(String basePath) {
        this.basePath = basePath;
    }

    public Map<String, Object> getDefinitions() {
        return definitions;
    }

    public void addDefinitions(String path, Object definition) {
        definitions.put(path, definition);
    }

    public Set<Tag> getTags() {
        return tags;
    }

    public Map<String, Map<String, Path>> getPaths() {
        return paths;
    }

    public Map<String, Object> getSecurityDefinitions() {
        return securityDefinitions;
    }

    public List<Map<String, Object>> getSecurity() {
        return security;
    }

    public void addSecurityDefinitions(Map<String, Object> map) {
        securityDefinitions.putAll(map);
    }

    public void addSecurity(Map<String, Object> map) {
        security.add(map);
    }

    public static class Concat {

        private String name;

        private String url;

        private String email;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public String getEmail() {
            return email;
        }

        public void setEmail(String email) {
            this.email = email;
        }
    }

    public static class Info {

        private String description;

        private String version;

        private String title;

        private License license;

        private Concat concat;

        public Info(String description, String version, String title, License license, Concat concat) {
            this.description = description;
            this.version = version;
            this.title = title;
            this.license = license;
            this.concat = concat;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public String getVersion() {
            return version;
        }

        public void setVersion(String version) {
            this.version = version;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public License getLicense() {
            return license;
        }

        public void setLicense(License license) {
            this.license = license;
        }

        public Concat getConcat() {
            return concat;
        }

        public void setConcat(Concat concat) {
            this.concat = concat;
        }
    }

    public static class Path {

        private List<String> tags = new ArrayList<>();

        private String summary;

        private String description;

        private final String operationId;

        private List<String> produces = new ArrayList<>();

        private List<String> consumes = new ArrayList<>();

        private List<Map<String, Object>> parameters = new ArrayList<>();

        private Map<String, Object> responses = new HashMap<>();

        public Path(String operationId) {
            this.operationId = operationId;
        }

        public void addProduce(String produce) {
            this.produces.add(produce);
        }

        public void addConsume(String consume) {
            this.consumes.add(consume);
        }

        public void addParameter(Map<String, Object> parameter) {
            this.parameters.add(parameter);
        }

        public String getOperationId() {
            return operationId;
        }

        public void addResponse(String status, Object object) {
            Map<String, Object> response = new HashMap<>();
            response.put("description", "OK");
            response.put("schema", doProcessSchema(object));
            response.put("example", object);
            this.responses.put(status, response);
        }

        public List<String> getTags() {
            return tags;
        }

        public void setTags(List<String> tags) {
            this.tags = tags;
        }

        public void addTag(String tag) {
            this.tags.add(tag);
        }

        public String getSummary() {
            return summary;
        }

        public void setSummary(String summary) {
            this.summary = summary;
        }

        public List<String> getProduces() {
            return produces;
        }

        public void setProduces(List<String> produces) {
            this.produces = produces;
        }

        public List<String> getConsumes() {
            return consumes;
        }

        public void setConsumes(List<String> consumes) {
            this.consumes = consumes;
        }

        public List<Map<String, Object>> getParameters() {
            return parameters;
        }

        public void setParameters(List<Map<String, Object>> parameters) {
            this.parameters = parameters;
        }

        public Map<String, Object> getResponses() {
            return responses;
        }

        public void setResponses(Map<String, Object> responses) {
            this.responses = responses;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }
    }

    public static class Parameter {

        private String name;

        private String in;

        private boolean required = false;

        private String type;

        private Object schema;

        private String description;

        private Object example;

        public Parameter(boolean required, String name, String in, String type, String description, Object example) {
            this.name = name;
            this.in = in;
            this.type = type;
            this.description = description;
            this.required = required;
            if ("body".equalsIgnoreCase(in)) {
                this.schema = "";
            } else {
                this.example = example;
                /*
                 * fix swagger文档使用knife4j时无法显示接口详情的问题（query类型参数）
                 * schema 需设置为空字符串，否则请求参数中数据类型字段显示不正确
                 */
                this.schema = "";
            }
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getIn() {
            return in;
        }

        public void setIn(String in) {
            this.in = in;
        }

        public boolean isRequired() {
            return required;
        }

        public void setRequired(boolean required) {
            this.required = required;
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public Object getSchema() {
            return schema;
        }

        public void setSchema(Object schema) {
            this.schema = schema;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        public Object getExample() {
            return example;
        }

        public void setExample(Object example) {
            this.example = example;
        }
    }

    public static class Tag {

        private String name;

        private String description;

        public Tag(String name, String description) {
            this.name = name;
            this.description = description;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getDescription() {
            return description;
        }

        public void setDescription(String description) {
            this.description = description;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }
            if (o == null || getClass() != o.getClass()) {
                return false;
            }
            Tag tag = (Tag) o;
            return Objects.equals(name, tag.name);
        }

        @Override
        public int hashCode() {
            return Objects.hash(name);
        }
    }

    public static class License {

        private String name;

        private String url;

        public License(String name, String url) {
            this.name = name;
            this.url = url;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }
    }

    public static class BasicAuth {

        public final static String KEY_NAME = "BasicAuth";

        /**
         * 类型,默认值
         */
        private String type = "basic";

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }
    }

    public static class ApiKeyAuth {

        public final static String KEY_NAME = "ApiKeyAuth";

        private String type = "apiKey";

        private String name = "header";

        private String in = "X-API-Key";

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getIn() {
            return in;
        }

        public void setIn(String in) {
            this.in = in;
        }
    }

    public static class OAuth2 {

        public final static String KEY_NAME = "OAuth2";

        private String type = "oauth2";

        private String flow;

        private String authorizationUrl;

        private String tokenUrl;

        private Map<String, String> scopes;

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public String getFlow() {
            return flow;
        }

        public void setFlow(String flow) {
            this.flow = flow;
        }

        public String getAuthorizationUrl() {
            return authorizationUrl;
        }

        public void setAuthorizationUrl(String authorizationUrl) {
            this.authorizationUrl = authorizationUrl;
        }

        public String getTokenUrl() {
            return tokenUrl;
        }

        public void setTokenUrl(String tokenUrl) {
            this.tokenUrl = tokenUrl;
        }

        public Map<String, String> getScopes() {
            return scopes;
        }

        public void setScopes(Map<String, String> scopes) {
            this.scopes = scopes;
        }
    }
}
